package com.qianfeng.smartdevice.service.impl;


//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼            BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qianfeng.smartdevice.cache.AreaCache;
import com.qianfeng.smartdevice.cache.CategoriesCache;
import com.qianfeng.smartdevice.constans.ResultCode;
import com.qianfeng.smartdevice.event.DeviceStatusChangeEvent;
import com.qianfeng.smartdevice.event.PowerCommandEvent;
import com.qianfeng.smartdevice.exceptions.QueryDataException;
import com.qianfeng.smartdevice.mapper.DeviceMapper;
import com.qianfeng.smartdevice.pojo.Area;
import com.qianfeng.smartdevice.pojo.Categories;
import com.qianfeng.smartdevice.pojo.Devices;
import com.qianfeng.smartdevice.service.IDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

/**
 * Created by jackiechan on 2021/6/9 16:01
 *
 * @author jackiechan
 */
@Service
@EnableAspectJAutoProxy(proxyTargetClass = true)//将代理方式改成 cglib
@Transactional
public class DeviceServiceImpl implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private CategoriesCache categoriesCache;
    @Autowired
    private AreaCache areaCache;


    private ObjectMapper objectMapper;

    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }


    @Override
    public PageInfo<Devices> findAllDevice(int page, int limit) {
        PageHelper.startPage(page, limit);
        //这个数据只有自己的基本信息,分类和区域信息都没有
        List<Devices> devicesList = deviceMapper.findAllDevices();
        //从缓存中获取到相关信息,设置到对象

        devicesList.forEach(devices -> {
            Long areaid = devices.getAreaid();//区域 id
            Long categoryid = devices.getCategoryid();//设备 id

            Area area = areaCache.getObj(areaid);//从缓存中获取指定的区域
            Categories categories = categoriesCache.getObj(categoryid);//从分类缓存中获取分类
            //将从缓存中获取到的区域和分类设置到当前遍历的对象上面
            devices.setArea(area);
            devices.setCategory(categories);
        });

        PageInfo<Devices> pageInfo = new PageInfo<>(devicesList);
        return pageInfo;
    }

    @Override
    public void sendControl(String uuid, String command) throws JsonProcessingException {
        //我们的目的是向设备发送命令
        //但是command 并不是真正的命令,它只是命令对应的一个 key,需要从分类中获取真正的命令
        //分类怎么获取? 每个设备对应一个分类,我们需要从设备中获取
        //设备怎么获取?我们有一个唯一的 uuid,根据 uuid 可以查询到设备的分类 id,然后我们有分类缓存,可以根据分类 id 从缓存中获取到分类的数据
        Long categoryId = deviceMapper.getCategoryIdByUUID(uuid);
        if (categoryId == null) {
            //说明传递的设备的 uuid 是错误的
            throw new QueryDataException("设备编号不存在", ResultCode.DATA_NOT_EXIST);
        }
        //在分类的对象中有一个属性记录了命令的映射,我们可以获取到
        Categories categories = categoriesCache.getObj(categoryId);
        String txcommand = categories.getTxcommand();//获取到指令的映射,是一个 json 字符串
        Map map = objectMapper.readValue(txcommand, Map.class);//将指令解析为 map
        Object o = map.get(command);//从 json 中读取到我们传递的命令对应的数据
        if (o instanceof Map) {
            Object type = ((Map<?, ?>) o).get("type");
            Object value = ((Map<?, ?>) o).get("value");
            if (type.toString().equals("1")) {
                //执行的是向继电器设备发送指令,发送的真正的指令内容就是 value
                //向设备发送数据是通过 webscoket 实现的,所以在这里我们需要找到这个设备对应的 websocket
                //方式 1 要么我们在这边获取到保存所有会话的 map,通过 uuid 拿连接,发送指令
                //方式 2 让 websocket 自己执行,在它内部可以直接获取到所有的会话的 map
                //我个人觉得方式 2 比较合适,因为可以不用和 websocket 直接建立关系,降低耦合度
                context.publishEvent(new PowerCommandEvent(uuid, value.toString()));
            } else if (type.toString().equals("2")) {
                //向颜色控制器发送指令
            }
        }

    }

    /**
     * 注意,我们这么写可能会出现异常
     * org.springframework.beans.factory.BeanInitializationException: Failed to process @EventListener annotation on bean with name 'deviceServiceImpl'; nested exception is java.lang.IllegalStateException: Need to invoke method 'onEvent' declared on target class 'DeviceServiceImpl', but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.
     * 大体的意思就是 默认情况下 aop 使用的是 jdk 原生的代理,需要必须包含接口,拦截的方法必须都在接口中存在,现在我们这个方法不在接口中,所以出现了异常 ,要么声明到接口中,要么修改代理方式为 cglib
     *
     * @param event
     */
    @EventListener
    @Async//异步执行
    public void onEvent(DeviceStatusChangeEvent event) {
        String devicesuuid = event.getDevicesuuid();
        String status = event.getStatus();
        //更新状态
        int result = deviceMapper.updateStatusByUUID(devicesuuid, status);
        System.err.println("设备:" + devicesuuid + "的更新结果是:===>" + result);
    }
}
